#include <stdio.h>
#include <unistd.h>    //close函数头文件
#include <sys/types.h> //open函数头文件
#include <sys/stat.h>  //open函数头文件
#include <fcntl.h>     //flags参数头文件
#include <elf.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "main.h"
/**
 * 
 * 这是一个 在自写操作系统项目中 对opjcopy原理的实验
 * 1根据elf文件头 找到所有程序头文件地址 大小
 * 2判断 并找到 程序头的虚拟内存地址是为 0x7e00 的程序头
 * 3把该程序头文件偏移地址 到 文件偏移地址+大小的范围 写入copy_bin.bin
 * 4判断 os.bin和copy_bin.bin的内容是否相同
 *  
 */

 /**
  * 
  * 对上述内容 `2` 中0x7e00的解释
  * os.bin来自objcopy可执行程序和os.elf,是os.elf的裸可执行文件版本
  * os.elf使用,obj文件和lds连接脚本链接生成
  * .lds连接脚本有如下内容
  * `
  * ENTRY(_start)
  * SECTIONS
  * {
  *	 . = 0x7e00;
  *  __begin_text = .;
  *  .text : ALIGN(4) { *(.text) }
  *  .................................
  * }	 
  * `
  * 这会使_start以及其它符号以0x7e00作为偏移量
  * 
  * 操作系统bootstage1会把os.bin加载到0x7e00物理地址然后会跳转到该地址执行
  * .lds连接脚本使得os.bin文件被加载到0x7e00处后所有指令都符合该地址
  * 比如:
  * 7e00: call   0x84db //_start: call kernelmain;
  * 这样是可执行的,因为kernelmain函数的汇编代码随着os.bin被加载到0x84db处 
  * 但是 如果 偏移是 0将会是：
  * 7e00：call 0x06DB  //_start: call kernelmain; 
  * 0x06DB == 0x84DB -7E00
  */


void add_need_free(void *need)
{
    need_free[need_free_index] = need;
    need_free_index++;
}

void add_need_close(int need)
{
    need_close[need_close_index] = need;
    need_close_index++;
}

void out()
{

    for (int i = 0; i < need_free_index; i++)
    {
        free(need_free[i]);
    }
    for (int i = 0; i < need_close_index; i++)
    {
        int ret = close(need_close[i]);
        print_info();
        printf("close %d %d\n",need_close[i],ret);
        if ( ret < 0)
        {
            
            my_perror(strerror(errno));
        };
    }
}

void print_green(char *s)
{
    printf(" \033[32m%s\033[0m", s);
}
void print_yellow(char *s)
{
    printf(" \033[33m%s\033[0m", s);
}
void print_red(char *s)
{
    printf(" \033[31m%s\033[0m", s);
}

void print_info(){
    print_green("[INFO]: ");
}

void print_success()
{
    print_green("[SUCCESS]: ");
}

void my_perror(char * s)
{
    // print_red("[ERROR]: ");
    printf("%s\n",s);
}


int main()
{

    int elf_fd = open("os.elf", O_RDONLY);
    if (elf_fd < 0)
    {
        my_perror(strerror(errno));
        goto out;
    }

    add_need_close(elf_fd);

    Elf32_Ehdr *p_ehdr = malloc(sizeof(Elf32_Ehdr));
    if (!p_ehdr)
    {
        my_perror(strerror(errno));
        goto out;
    }
    add_need_free(p_ehdr);

    int read_count = read(elf_fd, p_ehdr, sizeof(Elf32_Ehdr));
    if (read_count != sizeof(Elf32_Ehdr))
    {
        my_perror(strerror(errno));
        goto out;
    }
    printf("read count %d\n", read_count);

    unsigned int elf_header_size = p_ehdr->e_ehsize;
    unsigned int program_section_offset = p_ehdr->e_phoff;
    unsigned int program_section_size = p_ehdr->e_phentsize;
    unsigned int program_section_count = p_ehdr->e_phnum;
    print_info();
    printf("elf_header_size %d\n", elf_header_size);
    print_info();
    printf("program_section_offset %d\n", program_section_offset);
    print_info();
    printf("program_section_size: %d\n", program_section_size);
    print_info();
    printf("program_section_count: %d\n", program_section_count);

    Elf32_Phdr *p_phhd = malloc(sizeof(Elf32_Phdr));
    if (!p_phhd)
    {
        my_perror(strerror(errno));
        goto out;
    }
    add_need_free(p_phhd);

    unsigned int loop_count = program_section_size;
    while (loop_count > 0)
    {
        read_count = read(elf_fd, p_phhd, sizeof(Elf32_Phdr));
        if (read_count != sizeof(Elf32_Phdr))
        {
            my_perror(strerror(errno));
            goto out;
        }
        unsigned int my_vaddr = p_phhd->p_vaddr;
        if (my_vaddr != 0x7e00)
        {
            continue;
        }
        print_success();
        printf("detact correct program header 0x7e00\n");
        unsigned int file_pos_offset = p_phhd->p_offset;
        unsigned int size = p_phhd->p_filesz;

        print_info();
        printf("file_pos_offset: %d %#x\n", file_pos_offset, file_pos_offset);
        print_info();
        printf("size: %d %#x\n", size, size);
        unsigned int flags = p_phhd->p_flags;
        print_info();
        printf("flags: %d %#x -- ", flags, flags);
        if ((flags & PF_X) == PF_X)
        {
            /* code */
            printf("PF_X ");
        }

        if ((flags & PF_R) == PF_R)
        {
            /* code */
            printf("PF_R ");
        }
        if ((flags & PF_W) == PF_W)
        {
            /* code */
            printf("PF_W ");
        }
        printf("\n");

        if(lseek(elf_fd,file_pos_offset,SEEK_SET)<0){
            my_perror(strerror(errno));
            goto out;
        };

        void * buf = malloc(size);
        if (!buf){
            my_perror(strerror(errno));
            goto out;
        };
        add_need_free(buf);

        read_count = read(elf_fd,buf,size);
        if(read_count != size){
            my_perror(strerror(errno));
            goto out;
        }

        int copy_bin_fd = open("copy_bin.bin",O_CREAT|O_WRONLY|O_TRUNC,S_IRWXG|S_IRWXO|S_IRWXU);
        if (copy_bin_fd<0)
        {
            my_perror(strerror(errno));
            goto out;
        }
        add_need_close(copy_bin_fd);
        print_info();
        printf("open file copy_bin.bin\n");

        print_info();
        printf("write file copy_bin.bin\n");
        int write_count;
        if((write_count = write(copy_bin_fd,buf,size))==-1){
            my_perror(strerror(errno));
            goto out;
        };
        print_info();
        printf("write bytes %d\n",size);
        print_info();
        printf("write bytes count %d\n",write_count);
        if(size == write_count){
            print_success();
            printf("write success\n");
            goto out;
        }else{
            print_red("[WARN]: ");
            printf("write failed\n");
            my_perror(strerror(errno));
            goto out;
        }
    }

out:
    out();
    return 0;
}